package com.liruo.treasureattic.service.url.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liruo.treasureattic.common.httpmsg.exception.ValidatorException;
import com.liruo.treasureattic.common.httpmsg.result.Errno;
import com.liruo.treasureattic.common.model.dto.url.UrlAddReq;
import com.liruo.treasureattic.common.model.dto.url.UrlUpdateReq;
import com.liruo.treasureattic.common.model.entity.TreasureUrl;
import com.liruo.treasureattic.common.model.vo.url.UrlVo;
import com.liruo.treasureattic.common.util.NetUtils;
import com.liruo.treasureattic.common.util.ValidatorUtil;
import com.liruo.treasureattic.service.url.service.TreasureUrlService;
import com.liruo.treasureattic.service.url.mapper.TreasureUrlMapper;
import com.liruo.treasureattic.service.url.model.UrlConstant;
import com.liruo.treasureattic.starter.jwtlogin.holder.UserLoginStatusHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
* @author LYM
* @description 针对表【treasure_url(Can be the same level as folder)】的数据库操作Service实现
* @createDate 2022-10-31 20:29:01
*/
@Service
@Slf4j
public class TreasureUrlServiceImpl extends ServiceImpl<TreasureUrlMapper, TreasureUrl>
    implements TreasureUrlService {
    @Resource
    private UrlFolderServiceImpl urlFolderServiceImpl;
    /**
     * name只检测长度
     */
    @Override
    public long addUrl(UrlAddReq urlAddReq) {
        //检查参数
        ValidatorUtil.anyNull(urlAddReq);

        String urlName = urlAddReq.getName();
        String url = urlAddReq.getUrl();
        ValidatorUtil.anyNull(new String[]{urlName, url}, "url or urlName or open is null");
        ValidatorUtil.checkLen(urlName,
                UrlConstant.MIN_URL_NAME,
                UrlConstant.MAX_URL_NAME);
        ValidatorUtil.checkLen(url, 6, UrlConstant.MAX_URL);

        Long fid = urlAddReq.getFid();
        ValidatorUtil.requireGreat0(fid);


        urlFolderServiceImpl.folderExistAndNotDefault(Collections.singleton(fid));

        long userId  = UserLoginStatusHolder.getLoginStatus().getUserId();
        TreasureUrl treasureUrl = new TreasureUrl();
        BeanUtils.copyProperties(urlAddReq,treasureUrl);
        treasureUrl.setUid(userId);
        treasureUrl.setIco(NetUtils.getIcoFormUrl(treasureUrl.getUrl(), UrlConstant.DEFAULT_ICON));
        ValidatorUtil.dbOpt(this.save(treasureUrl),"fail when save url");
        return treasureUrl.getId();
    }
    /**
     * 因为有导入的url， 所以就不检查name了，只检测name的长度， 不需要进行拿出数据库的来对比是否需要更新
     */
    @Override
    public void updateUrl(UrlUpdateReq urlUpdateReq) {
        List<UrlUpdateReq> urlUpdateReqs = updateUrlCheck(Collections.singletonList(urlUpdateReq));
        if (urlUpdateReqs.isEmpty()) return;

        TreasureUrl newUrl = new TreasureUrl();
        BeanUtils.copyProperties(urlUpdateReqs.get(0), newUrl);
        this.updateById(newUrl);
    }

    /**
     * 先进行noDB检查，不与旧数据对比
     * @return 需要更新的url
     */
    private List<UrlUpdateReq> updateUrlCheck(List<UrlUpdateReq> urlUpdateReqs) {
        //检查参数
        ValidatorUtil.anyCollectionEmpty("update request is null",urlUpdateReqs);

        ArrayList<UrlUpdateReq> needUpdateList = new ArrayList<>(urlUpdateReqs.size());
        urlUpdateReqs.forEach(urlUpdateReq -> {
            Long urlId = urlUpdateReq.getId();
            ValidatorUtil.requireGreat0(urlId);

            Long updateFid = urlUpdateReq.getFid();
            String updateName = urlUpdateReq.getName();
            String updateUrl = urlUpdateReq.getUrl();
            String updateIco = urlUpdateReq.getIco();
            Boolean updateOpen = urlUpdateReq.getOpen();
            ValidatorUtil.allNull(
                    new String[]{updateName, updateUrl, updateIco},
                    "update  url can't all null",
                    updateOpen, updateFid);

            if(updateName != null){
                ValidatorUtil.checkLen(updateName,
                        UrlConstant.MIN_URL_NAME,
                        UrlConstant.MAX_URL_NAME);
            }
            if(updateUrl != null){
                ValidatorUtil.checkLen(updateUrl, 6,
                        UrlConstant.MAX_URL);
                if(urlUpdateReq.getIco() == null){
                    urlUpdateReq.setIco(NetUtils.getIcoFormUrl(urlUpdateReq.getUrl(), UrlConstant.DEFAULT_ICON));
                }
            }
            if(updateIco != null){
                ValidatorUtil.checkLen(updateIco, 6,
                        UrlConstant.MAX_URL);
            }
            needUpdateList.add(urlUpdateReq);
        });

        Long userId = UserLoginStatusHolder.getLoginStatus().getUserId();
        //判断url存在
        Set<Long> updateUrlIds = urlUpdateReqs.stream().map(UrlUpdateReq::getId).collect(Collectors.toSet());
        if(!isAllExists(updateUrlIds, userId)){
            throw new ValidatorException(Errno.PARAMS, "some url not exists");
        }
        //判断fid存在
        Set<Long> updateUrlFids = urlUpdateReqs.stream()
                .filter(req->req.getFid() != null)
                .peek(req->{
                    if(req.getFid() == UrlConstant.DEFAULT_FOLDER_FID){
                        throw new ValidatorException(Errno.PARAMS, "can' t add to default folder url==" + req.getName() + ": " + req.getName());
                    }
                })
                .map(UrlUpdateReq::getFid)
                .collect(Collectors.toSet());
        ValidatorUtil.requireGreat0(updateUrlFids);
        if(!urlFolderServiceImpl.isAllExists(updateUrlFids, userId)){
            throw new ValidatorException(Errno.PARAMS, "some father folder not exists");
        }
        return needUpdateList;
    }



    public boolean isAllExists(Set<Long> updateUrlIds, Long userId) {
        return count(new QueryWrapper<TreasureUrl>()
                .select("id")
                .eq("uid", userId)
                .in("id", updateUrlIds)) == updateUrlIds.size();
    }

    /**
     * 要同时减少url的父folder的url_cnt和默认folder的url_cnt
     */
    @Override
    public void delUrl(long urlId) {
        //检查参数
        ValidatorUtil.requireGreat0(urlId);

        Long userId = UserLoginStatusHolder.getLoginStatus().getUserId();
        //删除
        ValidatorUtil.dbOpt(this.remove(new UpdateWrapper<TreasureUrl>()
                .eq("uid", userId)
                .eq("id", urlId)
        ), "delete fail, not exists the url");
    }

    public boolean delUrlByFid(Set<Long> fid, long userId) {
        return this.remove(new UpdateWrapper<TreasureUrl>()
                .eq("uid", userId)
                .in("fid", fid)
        );
    }

    /**
     *
     * @return 一个folder的urls
     */
    @Override
    public List<UrlVo> getUrlByFid(Long fid){
        ValidatorUtil.requireGreat0(fid);
        Long userId = UserLoginStatusHolder.getLoginStatus().getUserId();
        return getUrlByFid(fid, false, userId)
                .stream().map(url->{
                    UrlVo urlVo = new UrlVo();
                    BeanUtils.copyProperties(url, urlVo);
                    return urlVo;
                }).toList();
    }
    public List<TreasureUrl> getUrlByFid(long fid, boolean open,  Long userId) {
        return list(new QueryWrapper<TreasureUrl>()
                .eq("uid", userId)
                .eq("fid", fid)
                .eq(open, "open", open)
        );
    }
    public List<Long> getUrlIdByFids(Collection<Long> fids, Long userId) {
        return list(new QueryWrapper<TreasureUrl>()
                .select("id")
                .eq("uid", userId)
                .in("fid", fids)
        ).stream().map(TreasureUrl::getId).toList();
    }


    //batch 操作


    /**
     * 不与旧数据对比
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchUpdate(List<UrlUpdateReq> batchUpdateUrlList) {
        ValidatorUtil.anyCollectionEmpty("batch update url can't null" ,batchUpdateUrlList);
        updateUrlCheck(batchUpdateUrlList);

        List<TreasureUrl> treasureUrls = batchUpdateUrlList.stream().map(url -> {
                    TreasureUrl treasureUrl = new TreasureUrl();
                    BeanUtils.copyProperties(url, treasureUrl);
                    return treasureUrl;
                })
                .toList();
        ValidatorUtil.dbOpt(this.updateBatchById(treasureUrls),
                "fail when batch update url");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchDelUrl(List<Long> deleteUrlIds) {
        ValidatorUtil.requireGreat0(deleteUrlIds);
        ValidatorUtil.dbOpt(this.removeByIds(deleteUrlIds),
                "fail when batch delete url");
    }

    public List<TreasureUrl> getLast(int size) {
        Long userId = UserLoginStatusHolder.getLoginStatus().getUserId();
        return this.list(new QueryWrapper<TreasureUrl>()
                .eq("uid", userId)
                .orderByDesc("id")
                .last("limit " + size)
        );
    }
}




